import { DeviceList } from './DeviceData';
import { DeviceInfo } from './Device';
import { format } from './format.js';
import { generateUUID } from 'three/src/math/MathUtils.js';


function getTrueSn(sn:string | undefined) {
    if(sn) {
        return sn.split(",")[0]
    }

    return undefined;
}

interface Point {
    id: number,
    pos: number[],
    out: number[],
    in: number[],
}

type TPipe = {
    pos: [number, number],
    color: [string, string],
    hflip: boolean,
    vflip: boolean,
    type: 'tee' | 'elbow'
}

type MachineJson = {
    actorid: string,
    sn: string,
    device: string,
    pos: [
        number, number, number
    ],
    aabb: {
        min: [
            number, number, number
        ],
        max: [
            number, number, number
        ]
    },
    in?: number,
    out?: number,
    type?: "W" | "B",
}

type ExportFille = {
    points: { [key: string]: Point },
    machine: MachineJson[],
    pipes: { [key: string]: { radiu: number, color: number } },
    pipeorder: string[],
    actors: { [key: string]: MachineJson }
}

type ActorJson = {
    id?: string, sn?: string, device: string, pos?: number[], aabb?: {
        min: number[],
        max: number[],
    }
}

type ActorJsonList = ActorJson[];

const sortByDistance = (list: ActorJsonList, o: number[]): ActorJsonList => {
    // 计算距离的函数
    const calculateDistance = (pos: number[]): number => {
        let [x, y, z] = pos;
        x -= o[0];
        y -= o[1];
        z -= o[2];
        return Math.sqrt(x * x + y * y + z * z);
    };

    // 排序函数
    const compareFn = (a: ActorJson, b: ActorJson): number => {
        const distanceA = calculateDistance(a.pos || [0,0,0]);
        const distanceB = calculateDistance(b.pos || [0,0,0]);

        if (distanceA < distanceB) {
            return -1;
        } else if (distanceA > distanceB) {
            return 1;
        } else {
            return 0;
        }
    };

    // 调用 sort() 方法进行排序
    return list.sort(compareFn);
};

const __getDeviceInfo = (device: string | undefined) => {
    if (device == undefined) return undefined;
    for (let i = 0; i < DeviceList.length; i++) {
        const e = DeviceList[i];
        if (e.deviceId == device) {
            return {
                deviceId: e.deviceId, //设备的唯一id吧
                group: e.group, //分类 组件/主设备
                name: e.name, //显示的名字
                match: e.match
            };
        }
    }
}

const FactorNum = 30;
const GridStep = 10;
const BottomWidth = 1100;
const UpWidth = 90 + 450;
const Offset = 180;
//冷冻供水 温度低
const chilledSupplyColor: [string, string] =  ['#53BFE7', '#006C9C'];
//冷冻回水
const chilledBackColor:[string, string] = ['#4D8ACF', '#013F86'];
//冷que供水 温度高
const coolSupplyColor: [string, string] = ['#E57753', '#5B0000'];
//冷que回水
const coolBackColor: [string, string] = ['#FDD653', '#6C5500'];

class Actor {
    id: string = "";
    pos: [number, number] = [0, 0]
    with: number = 10;
    height: number = 10;
    offsetY = 0;
    constructor(id: string) {
        this.id = id;
    }

    get minPos(): [number, number] {
        return [this.pos[0] - this.with * 0.5, this.pos[1] - this.height * 0.5 + this.offsetY]
    }

    get maxPos(): [number, number] {
        return [this.pos[0] + this.with * 0.5, this.pos[1] + this.height * 0.5 + this.offsetY]
    }

}

class OtherActor extends Actor {
    userdata: ActorJson | undefined = undefined;

    getDrawJson(ref: OutJson) {
        if (this.userdata) {
            ref.machine.push({
                sn: getTrueSn(this.userdata.sn),
                aabb: {
                    min: this.minPos,
                    max: this.maxPos
                },
                info: __getDeviceInfo(this.userdata.device),
                pointList: this.getPointList(),
            })
        }
    }

    getPointList(): PointInfo[]{
        if(this.userdata && this.userdata.device) {
            let info = __getDeviceInfo(this.userdata.device);
        }
        
        return[]
    }
}

class WaterPumpActor extends OtherActor {
    userdata: ActorJson | undefined = undefined;
    constructor(id: string) {
        super(id)
        this.with = 90
        this.height = 90
        this.offsetY = -10;
    }
    getDrawJson(ref: OutJson) {
        if (this.userdata) {
            ref.machine.push({
                sn: getTrueSn(this.userdata.sn),
                aabb: {
                    min: this.minPos,
                    max: this.maxPos
                },
                info: __getDeviceInfo(this.userdata.device),
                pointList: this.getPointList(),
            })

            let t = this.__addYaliji();
            t.pos[0] = this.pos[0] - 80;
            t.pos[1] = this.pos[1];
            t.getDrawJson(ref);
            t = this.__addYaliji();
            t.pos[0] = this.pos[0] + 80;
            t.pos[1] = this.pos[1];
            t.getDrawJson(ref);

        }
    }

    getPointList(): PointInfo[]{
        let needSwap = this.pos[0] > BottomWidth;
        let left = [this.pos[0] - 160, this.pos[1] - 30] as [number, number];
        let right = [this.pos[0] + 160, this.pos[1] - 30] as [number, number];
        if(needSwap) {
            let temp = left;
            left = right;
            right = temp;
        }
        return [
            {
                cnName: '进口压力',
                enName: 'inPress',
                pos: left,
            },
            {
                cnName: '出口压力',
                enName: 'outPress',
                pos: right,
            }
        ]
    }

    private __addYaliji(){
        let ac = new OtherActor(generateUUID())
        ac.userdata = {
            sn: '',
            device: 's_120',
        };
        ac.with = 30;
        ac.height = 67;
        return ac;
    }
}

class ChillerMachine extends OtherActor {
    //冷冻供水
    chilledWaterSupplyPort: number = -1;
    //冷冻回水
    chilledWaterBackPort: number = -1;
    //冷que供水
    coolWaterSupplyPort: number = -1;
    //冷que回水
    coolWaterBackPort: number = -1;


    private __points: { [key: string]: Point; } = {}
    private __allActors: { [key: string]: ActorJsonList; } = {}

    chilledSupplyActors: OtherActor[] = [];
    chilledBackActors: OtherActor[] = [];
    coolSupplyActors: OtherActor[] = [];
    coolBackActors: OtherActor[] = [];

    constructor(id: string) {
        super(id);
        this.with = 180;
        this.height = 80;
    }

    initData(points: { [key: string]: Point; }, allActors: { [key: string]: any; }) {
        this.__points = points;
        this.__allActors = allActors;
    }

    get chilledSupplyPos() {
        return [this.pos[0] + UpWidth, this.pos[1] - GridStep]
    }

    get chilledBackPos() {
        return [this.pos[0] + BottomWidth, this.pos[1] + GridStep]
    }

    get coolSupplyPos() {
        return [this.pos[0] - UpWidth, this.pos[1] - GridStep]
    }

    get coolBackPos() {
        return [this.pos[0] - BottomWidth, this.pos[1] + GridStep]
    }

    findAllActors() {
        let chilledSupply = this.__points["" + this.chilledWaterSupplyPort];
        if (chilledSupply) {
            this.chilledSupplyActors = [];
            this.__search(chilledSupply, true, this.chilledSupplyActors)
        }

        let chilledBack = this.__points["" + this.chilledWaterBackPort];
        if (chilledBack) {
            this.chilledBackActors = [];
            this.__search(chilledBack, false, this.chilledBackActors)
        }

        let coolSupply = this.__points["" + this.coolWaterSupplyPort];
        if (coolSupply) {
            this.coolSupplyActors = [];
            this.__search(coolSupply, true, this.coolSupplyActors)
        }

        let coolBack = this.__points["" + this.coolWaterBackPort];
        if (coolBack) {
            this.coolBackActors = [];
            this.__search(coolBack, false, this.coolBackActors)
        }
    }

    private __addYaliji(){
        let ac = new OtherActor(generateUUID())
        ac.userdata = {
            sn: '',
            device: 's_120',
        };
        ac.with = 30;
        ac.height = 67;
        return ac;
    }

    private __addWendu(){
        let ac = new OtherActor(generateUUID())
        ac.userdata = {
            sn: '',
            device: 's_121',
        };
        ac.with = 50;
        ac.height = 60;
        return ac;
    }

    private __addReliang(){
        let ac = new OtherActor(generateUUID())
        ac.userdata = {
            sn: '',
            device: 's_118',
        };
        ac.with = 40;
        ac.height = 60;
        return ac;
    }

    private __needDisplayActor(deviceId: string) {
        const info = __getDeviceInfo(deviceId);
        //if (info?.match) return true;
        if (info?.group?.startsWith("主设备/水泵")) return true;
        //if (info?.group?.startsWith("阀门类/机械仪表")) return true;
        //if(info?.name?.indexOf("蝶阀") != -1) return true;
        return false;
    }

    private __search(point: Point, isOut: boolean, outActors: OtherActor[]) {
        if (!point) return;
        if (isOut && point.out.length == 1) {
            let key = `${point.id}-${point.out[0]}`
            let actors = this.__allActors[key];
            actors = actors.filter((item, idx, list) => {
                return this.__needDisplayActor(item.device);
            })
            actors = sortByDistance(actors, point.pos);

            actors.forEach(a => {
                let ac = new WaterPumpActor(a.id || generateUUID())
                ac.userdata = a;
                outActors.push(ac);
            })

            this.__search(this.__points[point.out[0]], isOut, outActors);
        }

        if (!isOut && point.in.length == 1) {
            let key = `${point.in[0]}-${point.id}`
            let actors = this.__allActors[key];

            actors = actors.filter((item, idx, list) => {
                return this.__needDisplayActor(item.device);
            })

            actors = sortByDistance(actors, point.pos);

            actors.forEach(a => {
                let ac = new WaterPumpActor(a.id || generateUUID())
                ac.userdata = a;
                outActors.push(ac);
            })

            this.__search(this.__points[point.in[0]], isOut, outActors);
        }


    }

    getPointList(): PointInfo[]{
        let offst = this.with*0.5 + 50;
        let offst2 = offst + 100;
        return [
            {
                cnName: "蒸发器出水温度",
                enName: "leavingEvapWaterTemp",
                pos: [this.pos[0] + offst, this.pos[1] - 30]
            },
            {
                cnName: "蒸发器进水温度",
                enName: "enteringEvapWaterTemp",
                pos: [this.pos[0] + offst, this.pos[1] + 30]
            },
            {
                cnName: "冷凝器出水温度",
                enName: "leavingCondWaterTemp",
                pos: [this.pos[0] - offst, this.pos[1] - 30]
            },
            {
                cnName: "冷凝器进水温度",
                enName: "enterCondWaterTemp",
                pos: [this.pos[0] - offst, this.pos[1] + 30]
            },



            {
                cnName: "蒸发器出水压力",
                enName: "leavingEvapWaterPress",
                pos: [this.pos[0] + offst2, this.pos[1] - 30]
            },
            {
                cnName: "蒸发器进水压力",
                enName: "enteringEvapWaterPress",
                pos: [this.pos[0] + offst2, this.pos[1] + 30]
            },
            {
                cnName: "冷凝器出水压力",
                enName: "leavingCondWaterPress",
                pos: [this.pos[0] - offst2 - 180, this.pos[1] - 30]
            },
            {
                cnName: "冷凝器进水压力",
                enName: "enterCondWaterPress",
                pos: [this.pos[0] - offst2 - 180, this.pos[1] + 30]
            }
        ]
    }

    getDrawJson(ref: OutJson) {
        //

        ref.pipes.push({
            from: [this.pos[0], this.pos[1] + GridStep],
            to: [this.pos[0] + BottomWidth, this.pos[1] + GridStep],
            color: chilledBackColor
        })

        ref.pipes.push({
            from: [this.pos[0], this.pos[1] - GridStep],
            to: [this.pos[0] + UpWidth, this.pos[1] - GridStep],
            color: chilledSupplyColor
        })

        ref.pipes.push({
            from: [this.pos[0], this.pos[1] + GridStep],
            to: [this.pos[0] - BottomWidth, this.pos[1] + GridStep],
            color: coolBackColor
        })

        ref.pipes.push({
            from: [this.pos[0], this.pos[1] - GridStep],
            to: [this.pos[0] - UpWidth, this.pos[1] - GridStep],
            color: coolSupplyColor
        })


        super.getDrawJson(ref);


        // let offst = this.with*0.5 + 20;
        // let offst2 = offst + 100;
        let t = this.__addYaliji();
        t.pos[0] = this.pos[0] - 200 - this.with;
        t.pos[1] = this.pos[1] + GridStep;
        t.getDrawJson(ref);
        t = this.__addYaliji();
        t.pos[0] = this.pos[0] + 100 + this.with;
        t.pos[1] = this.pos[1] + GridStep;
        t.getDrawJson(ref);

        t = this.__addYaliji();
        t.pos[0] = this.pos[0] - 180 - this.with;
        t.pos[1] = this.pos[1] - GridStep;
        t.getDrawJson(ref);
        t = this.__addYaliji();
        t.pos[0] = this.pos[0] + 120 + this.with;
        t.pos[1] = this.pos[1] - GridStep;
        t.getDrawJson(ref);

        t = this.__addReliang();
        t.pos[0] = this.pos[0] + 300 + this.with;
        t.pos[1] = this.pos[1] - GridStep;
        t.getDrawJson(ref);

        t = this.__addWendu();
        t.pos[0] = this.pos[0] + 190 + this.with;
        t.pos[1] = this.pos[1] + GridStep;
        t.getDrawJson(ref);


        t = this.__addWendu();
        t.pos[0] = this.pos[0] + 210 + this.with;
        t.pos[1] = this.pos[1] - GridStep;
        t.getDrawJson(ref);


        for (let i = 0; i < this.coolBackActors.length; i++) {
            let xStep = (BottomWidth - this.with) / (this.coolBackActors.length + 1);
            const a = this.coolBackActors[i];
            a.pos = [-xStep * (i + 1) - this.with + this.pos[0] - Offset, this.pos[1] + GridStep];
            this.coolBackActors[i].getDrawJson(ref);
        }

        for (let i = 0; i < this.chilledBackActors.length; i++) {
            let xStep = (BottomWidth - this.with) / (this.chilledBackActors.length + 1);
            const a = this.chilledBackActors[i];
            a.pos = [xStep * (i + 1) + this.with + this.pos[0] + Offset, this.pos[1] + GridStep];
            this.chilledBackActors[i].getDrawJson(ref);
        }

        for (let i = 0; i < this.chilledSupplyActors.length; i++) {
            let xStep = (UpWidth - this.with) / (this.chilledSupplyActors.length + 1);
            const a = this.chilledSupplyActors[i];
            a.pos = [xStep * (i + 1) + this.with * 0.5 + this.pos[0], this.pos[1] - GridStep];
            this.chilledSupplyActors[i].getDrawJson(ref);
        }

        for (let i = 0; i < this.coolSupplyActors.length; i++) {
            let xStep = (UpWidth - this.with) / (this.coolSupplyActors.length + 1);
            const a = this.coolSupplyActors[i];
            a.pos = [-xStep * (i + 1) - this.with * 0.5 + this.pos[0], this.pos[1] - GridStep];
            this.coolSupplyActors[i].getDrawJson(ref);
        }
    }
}

type PointInfo = {
    cnName: string, enName: string, unit?: 'kPa'|' ℃', pos: [number, number] 
}

type OutJson = {
    tee: TPipe[];
    pipes: { from: [number, number], to: [number, number], color: [string, string] }[];
    machine: {
        aabb: {
            min: [
                number, number
            ],
            max: [
                number, number
            ]
        },
        sn?: string,
        info?: DeviceInfo,
        pointList?: PointInfo[]
    }[]
}

export class ConfigureTool {
    data: ExportFille;
    __chillerMachine: Map<string, ChillerMachine> = new Map();

    outjson: OutJson = {
        pipes: [],
        machine: [],
        tee: [],
    }

    constructor(data: ExportFille) {
        this.data = data;
    }

    private __getOrCreate<T>(map: Map<string, T>, id: string, constructable: Constructable<T>): T {
        if (!map.has(id)) {
            map.set(id, new constructable(id));
            return map.get(id) as T;
        }
        else {
            return map.get(id) as T;
        }

    }

    onInit() {
        this.outjson = {
            pipes: [],
            machine: [],
            tee: [],
        }
        const machineList = this.data.machine;
        for (let index = 0; index < machineList.length; index++) {
            const m = machineList[index];

            let info = this.__getOrCreate<ChillerMachine>(this.__chillerMachine, m.actorid, ChillerMachine);
            info.initData(this.data.points, this.data.actors)
            info.userdata = {
                id: m.actorid,
                sn: getTrueSn(m.sn),
                aabb: m.aabb,
                device: m.device,
                pos: m.pos,
            }
            if (m.type == "B") {
                info.chilledWaterSupplyPort = m.out || -1
                info.chilledWaterBackPort = m.in || -1
            }
            else {
                info.coolWaterSupplyPort = m.out || -1
                info.coolWaterBackPort = m.in || -1
            }
            info.pos = [(m.aabb.min[0] + m.aabb.max[0]) * FactorNum * 0.5, (m.aabb.min[2] + m.aabb.max[2]) * FactorNum * 0.5];
        }


        let idx = 0;
        let allcount = this.__chillerMachine.size;
        this.__chillerMachine.forEach((info, key) => {
            info.findAllActors();
            info.pos = [BottomWidth+ 50, idx * 200 + 480];
            idx++

            info.getDrawJson(this.outjson);
            if (idx == allcount) {
                this.outjson.pipes.push({
                    from: [info.chilledSupplyPos[0], info.chilledSupplyPos[1]],
                    to: [info.chilledSupplyPos[0], 280],
                    color: chilledSupplyColor
                })

                this.outjson.pipes.push({
                    from: [info.chilledBackPos[0], info.chilledBackPos[1]],
                    to: [info.chilledBackPos[0], 280],
                    color: chilledBackColor
                })

                this.outjson.pipes.push({
                    from: [info.coolSupplyPos[0], info.coolSupplyPos[1]],
                    to: [info.coolSupplyPos[0], 280],
                    color: coolSupplyColor
                })


                this.outjson.pipes.push({
                    from: [info.coolBackPos[0], info.coolBackPos[1]],
                    to: [info.coolBackPos[0], 280],
                    color: coolBackColor
                })

                this.outjson.tee.push({
                    type: 'elbow',
                    pos: [info.chilledSupplyPos[0], info.chilledSupplyPos[1]],
                    color: chilledSupplyColor,
                    hflip: false,
                    vflip: false,
                })

                this.outjson.tee.push({
                    type: 'elbow',
                    pos: [info.chilledBackPos[0], info.chilledBackPos[1]],
                    color: chilledBackColor,
                    hflip: false,
                    vflip: false,
                })

                this.outjson.tee.push({
                    type: 'elbow',
                    pos: [info.coolSupplyPos[0], info.coolSupplyPos[1]],
                    color: coolSupplyColor,
                    hflip: true,
                    vflip: false,
                })

                this.outjson.tee.push({
                    type: 'elbow',
                    pos: [info.coolBackPos[0], info.coolBackPos[1]],
                    color: coolBackColor,
                    hflip: true,
                    vflip: false,
                })
            }
            else {
                this.outjson.tee.push({
                    type: 'tee',
                    pos: [info.chilledSupplyPos[0], info.chilledSupplyPos[1]],
                    color: chilledSupplyColor,
                    hflip: false,
                    vflip: false,
                })

                this.outjson.tee.push({
                    type: 'tee',
                    pos: [info.chilledBackPos[0], info.chilledBackPos[1]],
                    color: chilledBackColor,
                    hflip: false,
                    vflip: false,
                })

                this.outjson.tee.push({
                    type: 'tee',
                    pos: [info.coolSupplyPos[0], info.coolSupplyPos[1]],
                    color: coolSupplyColor,
                    hflip: true,
                    vflip: false,
                })

                this.outjson.tee.push({
                    type: 'tee',
                    pos: [info.coolBackPos[0], info.coolBackPos[1]],
                    color: coolBackColor,
                    hflip: true,
                    vflip: false,
                })
            }
        })

        let t = this.__addCollector();
        t.pos[0] = BottomWidth + 50 + UpWidth + t.with*0.4;
        t.pos[1] = 280;
        t.getDrawJson(this.outjson);

        t = this.__addCollector();
        t.pos[0] = BottomWidth + 50 + BottomWidth - t.with*0.4;
        t.pos[1] = 280;
        t.getDrawJson(this.outjson);
    }


    private __addCollector() {
        let ac = new OtherActor(generateUUID())
        ac.userdata = {
            sn: '',
            device: 's_108',
        };
        ac.with = 300;
        ac.height = 180;
        return ac;
    }

    toJson() {
        //return JSON.stringify(this.outjson);
//   JSON.stringify({
//     source: "3d",
//     option: {
//       pc: newJson2,
//     },
//   })
let json2 = format(this.outjson);
        return JSON.stringify({
            source: "3d",
            option: {
                pc: json2,
            },
        })
    }
}

interface Constructable<T> {
    new(...args: any[]): T;
}

